package compute

import "C"
import (
	"gitee.com/amoyx/cloud-provider/pkg/cloud/tencent/models"
	"github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common"
	"github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common/profile"
	qcvm "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/cvm/v20170312"
	"strings"
)

type CvmGreeter interface {
	Cvm(region string) CvmInterface
}

type CvmInterface interface {
	Create(cvmRequest models.CvmRequest) (list []string, err error)
	Update()
	Delete(id string, isDeleteDataDisk bool) (bool, error)
	InstanceTypeList() (list []models.InstanceTypeConfig, err error)
	Get(id string) (result models.CvmResponse)
	ResetPassword(ids []string, username string, password string) (err error)
	TerminalVncUrl(id string) (url string, err error)
	ListByName(name string) (list []models.CvmResponse)
	ListByVpcId(vpcId string) (list []models.CvmResponse)
	Images() (list []models.HostImage, err error)
	QueryInstanceExists(zone string, instanceType string) (list []*qcvm.InstanceTypeQuotaItem, err error)
	QueryPrepaidPrice(in models.CvmPriceRequest) (price models.CvmPriceResponse, err error)
	QueryPostpaidPrice(in models.CvmPriceRequest) (price models.CvmPriceResponse, err error)
	List() (list []models.CvmResponse)
}

// cvm 云主机
type cvm struct {
	region  string
	compute *Compute
	client  *qcvm.Client
}

func newCvm(compute *Compute, region string) CvmInterface {
	c := &cvm{
		compute: compute,
		region:  region,
	}
	cpf := profile.NewClientProfile()
	cpf.HttpProfile.Endpoint = string(CvmEndPoint)
	client, _ := qcvm.NewClient(c.compute.Credential(), region, cpf)
	c.client = client
	return c
}

func (c *cvm) Create(in models.CvmRequest) (list []string, err error) {
	request := qcvm.NewRunInstancesRequest()

	request.VirtualPrivateCloud = &qcvm.VirtualPrivateCloud{
		VpcId:    common.StringPtr(in.VpcId),
		SubnetId: common.StringPtr(in.SubnetId),
	}

	if in.InstanceChargeType != "" {
		request.InstanceChargeType = common.StringPtr(in.InstanceChargeType)
	}

	if in.InstanceChargeType == "PREPAID" {
		request.InstanceChargePrepaid = &qcvm.InstanceChargePrepaid{
			Period:    common.Int64Ptr(in.Period),
			RenewFlag: common.StringPtr(in.RenewFlag),
		}
	}
	request.Placement = &qcvm.Placement{
		Zone: common.StringPtr(in.Zone),
	}
	request.InstanceType = common.StringPtr(in.InstanceType)
	request.ImageId = common.StringPtr(in.ImageId)
	request.SystemDisk = &qcvm.SystemDisk{
		DiskType: common.StringPtr(in.SystemDisk.DiskType),
		DiskSize: common.Int64Ptr(in.SystemDisk.DiskSize),
	}

	var dataDisks []*qcvm.DataDisk
	for _, disk := range in.DataDisk {
		dataDisks = append(dataDisks, &qcvm.DataDisk{
			DiskType: common.StringPtr(disk.DiskType),
			DiskSize: common.Int64Ptr(disk.DiskSize),
		})
	}
	request.DataDisks = dataDisks
	request.InstanceCount = common.Int64Ptr(in.InstanceCount)
	request.InstanceName = common.StringPtr(in.InstanceName)
	request.LoginSettings = &qcvm.LoginSettings{
		Password: common.StringPtr(in.Password),
	}
	request.SecurityGroupIds = common.StringPtrs(in.SecurityGroupIds)
	if strings.TrimSpace(in.HostName) != "" {
		request.HostName = common.StringPtr(in.HostName)
	}
	response, err := c.client.RunInstances(request)
	if err != nil {
		return
	}

	for _, id := range response.Response.InstanceIdSet {
		list = append(list, *id)
	}
	return list, nil
}

func (c *cvm) Update() {

}

// QueryPrepaidPrice 查询预付费价格
func (c *cvm) QueryPrepaidPrice(in models.CvmPriceRequest) (price models.CvmPriceResponse, err error) {
	price, err = c.inquiryPrice("PREPAID", in)
	return
}

// QueryPostpaidPrice 查询后付费价格
func (c *cvm) QueryPostpaidPrice(in models.CvmPriceRequest) (price models.CvmPriceResponse, err error) {
	price, err = c.inquiryPrice("POSTPAID_BY_HOUR", in)
	return
}

func (c *cvm) QueryInstanceExists(zone string, instanceType string) (list []*qcvm.InstanceTypeQuotaItem, err error) {
	request := qcvm.NewDescribeZoneInstanceConfigInfosRequest()

	request.Filters = []*qcvm.Filter{
		&qcvm.Filter{
			Name:   common.StringPtr("instance-type"),
			Values: common.StringPtrs([]string{instanceType}),
		},
		&qcvm.Filter{
			Name:   common.StringPtr("zone"),
			Values: common.StringPtrs([]string{zone}),
		},
	}
	response, err := c.client.DescribeZoneInstanceConfigInfos(request)
	if err != nil {
		return
	}
	list = response.Response.InstanceTypeQuotaSet
	return
}

func (c *cvm) inquiryPrice(chargeType string, in models.CvmPriceRequest) (price models.CvmPriceResponse, err error) {
	request := qcvm.NewInquiryPriceRunInstancesRequest()
	request.InstanceChargeType = common.StringPtr(chargeType)
	if chargeType == "PREPAID" {
		request.InstanceChargePrepaid = &qcvm.InstanceChargePrepaid{
			Period: common.Int64Ptr(in.Period),
		}
	}
	request.Placement = &qcvm.Placement{
		Zone: common.StringPtr(in.Zone),
	}
	request.InstanceType = common.StringPtr(in.InstanceType)
	request.InstanceCount = common.Int64Ptr(in.InstanceCount)

	request.SystemDisk = &qcvm.SystemDisk{
		DiskType: common.StringPtr(in.SystemDisk.DiskType),
		DiskSize: common.Int64Ptr(in.SystemDisk.DiskSize),
	}
	var datadisks []*qcvm.DataDisk
	for _, val := range in.DataDisks {
		datadisks = append(datadisks, &qcvm.DataDisk{
			DiskType: common.StringPtr(val.DiskType),
			DiskSize: common.Int64Ptr(val.DiskSize),
		})
	}
	request.DataDisks = datadisks
	response, err := c.client.InquiryPriceRunInstances(request)
	if err != nil {
		return
	}
	instance := response.Response.Price.InstancePrice
	switch {
	case chargeType == "PREPAID":
		price.InstancePrice.DiscountPrice = *instance.DiscountPrice
		price.InstancePrice.Discount = *instance.Discount
	case chargeType == "POSTPAID_BY_HOUR":
		price.InstancePrice.UnitPrice = *instance.UnitPrice
		price.InstancePrice.UnitPriceDiscount = *instance.UnitPriceDiscount
	}
	return
}

func (c *cvm) Images() (list []models.HostImage, err error) {
	request := qcvm.NewDescribeImagesRequest()
	request.Limit = common.Uint64Ptr(100)
	request.Offset = common.Uint64Ptr(0)
	response, err := c.client.DescribeImages(request)
	if err != nil {
		return
	}
	for _, val := range response.Response.ImageSet {
		list = append(list, models.HostImage{
			ImageId:          *val.ImageId,
			OsName:           *val.OsName,
			ImageType:        *val.ImageType,
			CreatedTime:      *val.CreatedTime,
			ImageName:        *val.ImageName,
			ImageSize:        *val.ImageSize,
			Architecture:     *val.Architecture,
			ImageState:       *val.ImageState,
			Platform:         *val.Platform,
			ImageDescription: *val.ImageDescription,
			ImageCreator:     *val.ImageCreator,
			ImageSource:      *val.ImageSource,
		})
	}
	return list, nil
}

func (c *cvm) TerminalVncUrl(id string) (url string, err error) {
	request := qcvm.NewDescribeInstanceVncUrlRequest()
	request.InstanceId = common.StringPtr(id)
	response, err := c.client.DescribeInstanceVncUrl(request)
	if err != nil {
		return
	}
	url = *response.Response.InstanceVncUrl
	return "https://img.qcloud.com/qcloud/app/active_vnc/index.html?InstanceVncUrl=" + url, nil
}

func (c *cvm) Restart(id string) (string, error) {
	request := qcvm.NewRebootInstancesRequest()
	request.InstanceIds = common.StringPtrs([]string{id})
	response, err := c.client.RebootInstances(request)
	if err != nil {
		return "", err
	}
	return *response.Response.RequestId, nil
}

func (c *cvm) InstanceTypeList() (list []models.InstanceTypeConfig, err error) {
	request := qcvm.NewDescribeInstanceTypeConfigsRequest()
	response, err := c.client.DescribeInstanceTypeConfigs(request)
	if err != nil {
		return
	}
	for _, val := range response.Response.InstanceTypeConfigSet {
		list = append(list, models.InstanceTypeConfig{
			Zone:           *val.Zone,
			InstanceType:   *val.InstanceType,
			InstanceFamily: *val.InstanceFamily,
			GPU:            *val.GPU,
			CPU:            *val.CPU,
			Memory:         *val.Memory,
			FPGA:           *val.FPGA,
			GpuCount:       *val.GpuCount,
		})
	}
	return list, nil
}

func (c *cvm) Delete(id string, isDeleteDataDisk bool) (bool, error) {
	request := qcvm.NewTerminateInstancesRequest()
	request.InstanceIds = common.StringPtrs([]string{id})
	request.ReleasePrepaidDataDisks = common.BoolPtr(isDeleteDataDisk)
	_, err := c.client.TerminateInstances(request)
	if err != nil {
		return false, err
	}
	return true, nil
}

func (c *cvm) Get(id string) (result models.CvmResponse) {
	list := c.query(map[string]string{"instance-id": id})
	if len(list) > 0 {
		result = list[0]
	}
	return
}

func (c *cvm) ResetPassword(ids []string, username string, password string) (err error) {
	request := qcvm.NewResetInstancesPasswordRequest()
	request.InstanceIds = common.StringPtrs(ids)
	request.UserName = common.StringPtr(username)
	request.Password = common.StringPtr(password)
	_, err = c.client.ResetInstancesPassword(request)
	return
}

func (c *cvm) List() (list []models.CvmResponse) {
	return c.query(map[string]string{})
}

func (c *cvm) ListByName(name string) (list []models.CvmResponse) {
	return c.query(map[string]string{"instance-name": name})
}

func (c *cvm) ListByVpcId(vpcId string) (list []models.CvmResponse) {
	return c.query(map[string]string{"vpc-id": vpcId})
}

func (c *cvm) query(args map[string]string) (list []models.CvmResponse) {
	request := qcvm.NewDescribeInstancesRequest()
	var filters []*qcvm.Filter
	for key, val := range args {
		filters = append(filters, &qcvm.Filter{
			Name:   common.StringPtr(key),
			Values: common.StringPtrs([]string{val}),
		})
	}
	request.Filters = filters
	request.Limit = common.Int64Ptr(100)
	response, err := c.client.DescribeInstances(request)
	if err != nil {
		return
	}

	for _, v := range response.Response.InstanceSet {
		disk := models.SystemDisk{
			DiskType: *v.SystemDisk.DiskType,
			DiskSize: *v.SystemDisk.DiskSize,
			DiskId:   *v.SystemDisk.DiskId,
		}
		var datadisks []models.DataDisk
		for _, d := range v.DataDisks {
			datadisks = append(datadisks, models.DataDisk{
				DiskId:             *d.DiskId,
				DiskType:           *d.DiskType,
				DiskSize:           *d.DiskSize,
				DeleteWithInstance: *d.DeleteWithInstance,
				SnapshotId:         *d.SnapshotId,
				Encrypt:            *d.Encrypt,
			})
		}
		var privateiplist []string
		for _, ip := range v.PrivateIpAddresses {
			privateiplist = append(privateiplist, *ip)
		}

		var publiciplist []string
		for _, ip := range v.PublicIpAddresses {
			publiciplist = append(publiciplist, *ip)
		}
		vpc := models.VirtualPrivateCloud{
			VpcId:        *v.VirtualPrivateCloud.VpcId,
			SubnetId:     *v.VirtualPrivateCloud.SubnetId,
			AsVpcGateway: *v.VirtualPrivateCloud.AsVpcGateway,
		}

		internetAccessible := models.InternetAccessible{
			InternetChargeType:      *v.InternetAccessible.InternetChargeType,
			InternetMaxBandwidthOut: *v.InternetAccessible.InternetMaxBandwidthOut,
			PublicIpAssigned:        *v.InternetAccessible.PublicIpAssigned,
			BandwidthPackageId:      *v.InternetAccessible.BandwidthPackageId,
		}

		var sgids []string
		for _, g := range v.SecurityGroupIds {
			sgids = append(sgids, *g)
		}

		list = append(list, models.CvmResponse{
			InstanceId:          *v.InstanceId,
			InstanceType:        *v.InstanceType,
			CPU:                 *v.CPU,
			Memory:              *v.Memory,
			RestrictState:       *v.RestrictState,
			InstanceName:        *v.InstanceName,
			InstanceChargeType:  *v.InstanceChargeType,
			SystemDisk:          disk,
			DataDisks:           datadisks,
			PrivateIpAddresses:  privateiplist,
			PublicIpAddresses:   publiciplist,
			InternetAccessible:  internetAccessible,
			VirtualPrivateCloud: vpc,
			ImageId:             *v.ImageId,
			RenewFlag:           *v.RenewFlag,
			CreatedTime:         *v.CreatedTime,
			ExpiredTime:         *v.ExpiredTime,
			OsName:              *v.OsName,
			SecurityGroupIds:    sgids,
			InstanceState:       *v.InstanceState,
		})
	}
	return
}
